package com.hmkj.web.controller.trade;

import com.hmkj.common.baseMapper.GenericPo;
import com.hmkj.common.entity.ResultEntity;
import com.hmkj.common.page.BasePageParams;
import com.hmkj.common.page.Pages;
import com.hmkj.common.utils.ValidateUtils;
import com.hmkj.core.constant.CacheID;
import com.hmkj.core.constant.RiskNID;
import com.hmkj.core.exception.BussinessException;
import com.hmkj.core.mapper.order.OrderMapper;
import com.hmkj.core.model.order.OrderModel;
import com.hmkj.core.model.quote.SimpleRealData;
import com.hmkj.core.po.commo.Commo;
import com.hmkj.core.po.order.Order;
import com.hmkj.core.po.user.User;
import com.hmkj.core.po.user.UserFreeze;
import com.hmkj.core.service.cache.RedisService;
import com.hmkj.core.service.commo.CommoService;
import com.hmkj.core.service.order.OrderService;
import com.hmkj.core.service.risk.RiskParamService;
import com.hmkj.core.service.user.UserFreezeService;
import com.hmkj.core.service.user.UserService;
import com.hmkj.web.controller.GenericController;
import com.xiaoleilu.hutool.util.CollectionUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.*;

@Api(description = "策略交易相关")
@Slf4j
@RestController
@RequestMapping(GenericController.MEMBER_URL + "/trade")
public class OrderController extends GenericController<Integer, User> {
    @Resource
    private UserService userService;
    @Resource
    private RedisService redisService;
    @Resource
    private OrderService orderService;
    @Resource
    private RiskParamService riskParamService;
    @Resource
    private CommoService commoService;
    @Resource
    private UserFreezeService userFreezeService;


    @ApiOperation(value = "获取交易参数", notes = "获取交易参数")
    @ResponseBody
    @RequestMapping(value = "getTradeParams", method = RequestMethod.POST)
    public ResultEntity getTradeParams(@ApiParam("品种ID") @RequestParam final Integer commoId) throws ParseException {
        User user = userService.selectByPrimaryKey(getLoginUser().getId());
        if (user == null) throw new BussinessException("查询用户信息失败");
        // 当前交易品种
        Commo commo = redisService.get(CacheID.COMMO_ID_PREFIX + commoId, Commo.class);
        // 交易数量列表
        List<Integer> nums = riskParamService.getParam2IntList(RiskNID.TRADE_NUMBER, commoId);
        // 止损点数列表
        List<Integer> lostStopPoints = riskParamService.getParam2IntList(RiskNID.LOSE_STOP_POINT, commoId);
        // 每点合约价格
        BigDecimal pointVlaue = riskParamService.getParamValue(RiskNID.POINT_VALUE, commoId, BigDecimal.class);
        // 止盈倍数
        BigDecimal profitStopTimes = riskParamService.getParamValue(RiskNID.PROFIT_STOP_TIMES, commoId, BigDecimal.class);
        // 对人民币汇率
        BigDecimal exchangeRate = riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, commoId, BigDecimal.class);
        // 止损比例
        BigDecimal loseStopRate = riskParamService.getParamValue(RiskNID.LOSE_STOP_RATE, commoId, BigDecimal.class);
        // 每手手续费
        BigDecimal fee = riskParamService.getFeeByAgent(commoId, user.getAgentId());
        // 本段持仓截止时间
        String autoSettleTime = riskParamService.getAutoSettleTimeStr(commoId);
        // 行情最新价
        BigDecimal np = riskParamService.lastPrice(commoId);
        // 是否是交易时间
        boolean isTradeTime = riskParamService.isTradeTime(commoId);

        // 是否暂停交易
        boolean isTradePause = riskParamService.isTradePause(commoId);
        // 可以用积分
        Integer points = user.getPoint();

        return ok(m -> {
            m.put("code", commo.getCode());// 品种编码
            m.put("name", commo.getName());// 品种名称
            m.put("contract", riskParamService.getContractNo(commoId));// 合约号
            m.put("nums", nums);// 交易数量列表
            m.put("lostStopPoints", lostStopPoints);// 止损点数列表
            m.put("pointVlaue", pointVlaue);// 每点合约价格
            m.put("profitStopTimes", profitStopTimes);// 止盈倍数
            m.put("exchangeRate", exchangeRate);// 对人民币汇率
            m.put("loseStopRate", loseStopRate);// 止损比例
            m.put("fee", fee);// 每手手续费
            m.put("autoSettleTime", autoSettleTime);// 本段持仓截止时间
            m.put("np", np);// 行情最新价
            m.put("isTradeTime", isTradeTime && !isTradePause);// 是否可以交易
            m.put("points", points);// 可以用积分
        });
    }

    @ApiOperation(value = "策略下单", notes = "策略下单")
    @ResponseBody
    @RequestMapping(value = "/trade", method = RequestMethod.POST)
    public ResultEntity trade(@ApiParam("品种ID") @RequestParam final Integer commoId,
                              @ApiParam("交易数量") @RequestParam final Integer num,
                              @ApiParam("止损点数") @RequestParam final Integer lostPoint,
                              @ApiParam("积分抵扣") @RequestParam final Integer point,
                              @ApiParam("开仓类型 1市价 2限价") @RequestParam final Integer openWay,
                              @ApiParam("交易方向 1买涨 2买跌") @RequestParam final Integer direction,
                              @ApiParam("委托价格（限价单使用）") @RequestParam(required = false) final BigDecimal entrustPrice,
                              HttpServletRequest request) throws Exception {
        //校验用户充值是否冻结
        User user = getLoginUser();
        UserFreeze userFreeze = userFreezeService.selectOne(new UserFreeze(f -> {
            f.setDelFlag(UserFreeze.DELFLAG.NORMAL.code);
            f.setUserId(user.getId());
        }));
        if (null == userFreeze || UserFreeze.STATUS.T1.code != userFreeze.getFreezeTender()) {
            throw new BussinessException("您的账户策略交易功能已被冻结，请联系客服处理");
        }
        orderService.doTrade(user.getId(), commoId, num, lostPoint, point, openWay, direction, entrustPrice, getDomain(), Order.FOLLOW.T1.code,null, "47.93.207.165");
        return ok("下单成功", null);
    }


    @ApiOperation(value = "策略平仓", notes = "策略平仓")
    @ResponseBody
    @RequestMapping(value = "/cover", method = RequestMethod.POST)
    public ResultEntity cover(@ApiParam("订单ID") @RequestParam final Integer orderId,
                              @ApiParam("平仓数量") @RequestParam final Integer num) throws Exception {
        Order order = orderService.selectByPrimaryKey(orderId);
        if (order == null) throw new BussinessException("订单不存在");
        if (order.getStatus() != Order.STATUS.T3.code) throw new BussinessException("此订单状态不能进行该操作");
        if (!order.getUserId().equals(getLoginUser().getId())) throw new BussinessException("您无权操作此订单");
        if (order.getNumber() < num || num <= 0 || !ValidateUtils.isPositiveInteger(num)) throw new BussinessException("平仓数量不正确");
        if (!riskParamService.isTradeTime(order.getCommoId())) throw new BussinessException("当前非交易时间，下单失败");
//        orderService.coverOrder(order, num, Order.SETTLETYPE.T1);
        Map<String, Object> data = new HashMap<>();
        data.put("orderId", orderId);
        data.put("num", num);
        data.put("settleType", Order.SETTLETYPE.T1.code);
        redisService.push(CacheID.QUEUE_COVER_ORDER, data);
        return ok("操作成功", null);
    }

    @ApiOperation(value = "策略撤单", notes = "策略撤单")
    @ResponseBody
    @RequestMapping(value = "/cancel", method = RequestMethod.POST)
    public ResultEntity cancel(@ApiParam("订单ID") @RequestParam final Integer orderId) throws Exception {
        Order order = orderService.selectByPrimaryKey(orderId);
        if (order == null) throw new BussinessException("订单不存在");
        if (order.getStatus() != Order.STATUS.T2.code) throw new BussinessException("此订单状态不能进行该操作");
        if (!order.getUserId().equals(getLoginUser().getId())) throw new BussinessException("您无权操作此订单");
        orderService.cancelOrder(orderId, getLoginUser().getId(),Order.CANCLETYPE.T1);
        return ok("操作成功", null);
    }

    /**
     * 修改止盈止损
     */
    @ApiOperation(value = "修改止盈止损", notes = "修改止盈止损")
    @RequestMapping(value = "modifyProfitStop", method = RequestMethod.POST)
    public ResultEntity modifyProfitStop(@ApiParam("订单id") @RequestParam Integer orderId,
                                         @ApiParam("止盈金额") @RequestParam BigDecimal profitStop,
                                         @ApiParam("止损金额") @RequestParam BigDecimal loseStop) {
        Order order = orderService.selectByPrimaryKey(orderId);
        if (order == null) {
            throw new BussinessException("当前订单不存在");
        }
        if (!order.getUserId().equals(getLoginUser().getId())) {
            throw new BussinessException("您无权操作此订单");
        }
        if (order.getStatus().intValue() != Order.STATUS.T2.code&&order.getStatus().intValue() != Order.STATUS.T3.code) {
            throw new BussinessException("当前订单状态不能修改止盈止损");
        }
        if (order.getDirection().intValue() == Order.DIRECTION.T1.code) {
            //校验止盈价格
            if (profitStop.compareTo(order.getProfitStopMaxPrice()) > 0 || profitStop.compareTo(order.getLoseStopMinPrice()) < 0) {
                throw new BussinessException("止盈价格不能大于" + order.getProfitStopMaxPrice() + "且不能小于" + order.getLoseStopMinPrice());
            }
            //校验止损价格
            if (loseStop.compareTo(order.getLoseStopMinPrice()) < 0 || loseStop.compareTo(order.getProfitStopMaxPrice()) > 0) {
                throw new BussinessException("止盈价格不能大于" + order.getProfitStopMaxPrice() + "且不能小于" + order.getLoseStopMinPrice());
            }
            //校验止损价格
            if (loseStop.compareTo(profitStop) > 0) {
                throw new BussinessException("止盈价格不能小于止损价格");
            }
        } else {
            //校验止盈价格
            if (profitStop.compareTo(order.getProfitStopMaxPrice()) < 0 || profitStop.compareTo(order.getLoseStopMinPrice()) > 0) {
                throw new BussinessException("止盈价格不能大于" + order.getLoseStopMinPrice() + "且不能小于" + order.getProfitStopMaxPrice());
            }
            //校验止损价格
            if (loseStop.compareTo(order.getLoseStopMinPrice()) > 0 || loseStop.compareTo(order.getProfitStopMaxPrice()) < 0) {
                throw new BussinessException("止盈价格不能大于" + order.getLoseStopMinPrice() + "且不能小于" + order.getProfitStopMaxPrice());
            }
            if (loseStop.compareTo(profitStop) < 0) {
                throw new BussinessException("止损价格不能小于止盈价格");
            }
        }
        orderService.updateByPrimaryKeySelective(new Order(o -> {
            o.setId(order.getId());
            o.setProfitStopPrice(profitStop);
            o.setLoseStopPrice(loseStop);
        }));
        return ok();
    }

    /**
     * 策略列表（委托、持仓、结算，委托、结算不分页）
     *
     * @param follow
     * @param status
     * @param basePageParams
     * @return
     */
    @ApiOperation(value = "策略列表", notes = "策略列表")
    @ResponseBody
    @RequestMapping(value = "list", method = RequestMethod.POST)
    public ResultEntity list(@ApiParam("策略类型 1点买策略 2跟单策略") @RequestParam Integer follow,
                             @ApiParam("状态 2委托列表 3持仓列表 5结算列表") @RequestParam Integer status,
                             @ModelAttribute BasePageParams basePageParams) {

        Map<Integer, BigDecimal> exMap = new HashMap<>();
        Map<Integer, BigDecimal> waMap = new HashMap<>();
        Map<Integer, BigDecimal> poMap = new HashMap<>();

        if (status == 5) { // 分页查询结算列表
            Map<String, Object> param = new HashMap<>();
            param.put("follow", follow);
            param.put("status", Order.STATUS.T4.code);// 状态5弃用，4代表结算中
            param.put("status2", Order.STATUS.T6.code);
            param.put("userId", getLoginUser().getId());
            param.put("delFlag", Order.DELFLAG.NORMAL.code);
            param.put("basePageParams", basePageParams);
            Pages<OrderModel> pages = orderService.selectByPage(basePageParams,
                    p -> ((OrderMapper) p).findCountForOrderList(param),
                    p -> ((OrderMapper) p).findListForOrderList(param));
            List<OrderModel> models = pages.getRows();

            if (models != null && models.size() > 0) {
                models.forEach(orderModel -> {
                    BigDecimal exchangeRate = exMap.containsKey(orderModel.getCommoId()) ? exMap.get(orderModel.getCommoId()) : riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, orderModel.getCommoId(), BigDecimal.class);
                    BigDecimal waveMin = waMap.containsKey(orderModel.getCommoId()) ? waMap.get(orderModel.getCommoId()) : riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, orderModel.getCommoId(), BigDecimal.class);

                    exMap.put(orderModel.getCommoId(), exchangeRate);
                    waMap.put(orderModel.getCommoId(), waveMin);

                    orderModel.setExchangeRate(exchangeRate);
                    orderModel.setWaveMin(waveMin);
                });
            }
            pages.setRows(models);
            return ok(pages);
        } else { // 查询委托、持仓列表
            List<Order> list = orderService.select(new Order(t -> {
                t.setDelFlag(GenericPo.DELFLAG.NORMAL.code);
                t.setStatus(status);
                t.setFollow(follow);
                t.setUserId(getLoginUser().getId());
            }));
            List<OrderModel> mlist = new ArrayList<>();
            if (CollectionUtils.isNotEmpty(list)) {
                list.stream().sorted(Comparator.comparing(Order::getId).reversed()).forEach(order -> {
                    OrderModel model = new OrderModel();
                    BeanUtils.copyProperties(order, model);
                    BigDecimal exchangeRate = exMap.containsKey(model.getCommoId()) ? exMap.get(model.getCommoId()) : riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, model.getCommoId(), BigDecimal.class);
                    BigDecimal waveMin = waMap.containsKey(model.getCommoId()) ? waMap.get(model.getCommoId()) : riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, model.getCommoId(), BigDecimal.class);
                    BigDecimal pointValue = poMap.containsKey(model.getCommoId()) ? poMap.get(model.getCommoId()) : riskParamService.getParamValue(RiskNID.POINT_VALUE, model.getCommoId(), BigDecimal.class);

                    exMap.put(model.getCommoId(), exchangeRate);
                    waMap.put(model.getCommoId(), waveMin);
                    poMap.put(model.getCommoId(), pointValue);

                    model.setExchangeRate(exchangeRate);
                    model.setWaveMin(waveMin);

                    // 最新价
                    BigDecimal np = riskParamService.lastPrice(order.getCommoId());
                    model.setLastPrice(np);

                    // 计算策略盈亏 = （平仓价-开仓价）* 数量 * 每点合约价 / 最小波动点
                    BigDecimal profit = np.subtract(order.getOpenPrice()).multiply(new BigDecimal(order.getNumber())).multiply(pointValue).multiply(exchangeRate).divide(waveMin, 2, BigDecimal.ROUND_HALF_UP);
                    if (Order.DIRECTION.T2.code == order.getDirection().intValue()) {
                        profit = profit.negate();
                    }
                    model.setProfit(profit);

                    mlist.add(model);
                });
            }
            return ok(mlist);
        }
    }


    /**
     * 单个品种持仓情况
     *
     * @param commoId 品种ID
     * @return
     */
    @ApiOperation(value = "单个品种持仓情况", notes = "单个品种持仓情况")
    @ResponseBody
    @RequestMapping(value = "commoHolding", method = RequestMethod.POST)
    public ResultEntity commoHolding(@ApiParam("品种ID") @RequestParam Integer commoId) {
        List<Order> list = orderService.select(new Order(t -> {
            t.setUserId(getLoginUser().getId());
            t.setStatus(Order.STATUS.T3.code);
            t.setCommoId(commoId);
            t.setDelFlag(Order.DELFLAG.NORMAL.code);
        }));

        List holding = new ArrayList();
        list.forEach(item -> {
            Map<String, Object> entity = new HashMap<>();
            entity.put("p", item.getOpenPrice());
            entity.put("d", item.getDirection());
            entity.put("n", item.getNumber());
            holding.add(entity);
        });

        return ok(m -> {
            m.put("list", holding);
            m.put("pv", riskParamService.getParamValue(RiskNID.POINT_VALUE, commoId, BigDecimal.class));
            m.put("ex", riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, commoId, BigDecimal.class));
            m.put("wp", riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, commoId, BigDecimal.class));
        });
    }


    /**
     * 实时行情最新现价/总盈亏
     *
     * @param follow
     * @param status
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "行情最新现价/总盈亏", notes = "行情最新现价/总盈亏")
    @ResponseBody
    @RequestMapping(value = "nowPrice", method = RequestMethod.POST)
    public ResultEntity nowPrice(@ApiParam("策略类型 1点买策略 2跟单策略") @RequestParam Integer follow,
                                 @ApiParam("统计类型 3浮动盈亏 6实际盈亏") @RequestParam Integer status) throws Exception {
        List<Order> orders = orderService.select(new Order(t -> {
            t.setUserId(getLoginUser().getId());
            t.setStatus(status);
            t.setFollow(follow);
            t.setDelFlag(Order.DELFLAG.NORMAL.code);
        }));
        BigDecimal profit = BigDecimal.ZERO;

        if (CollectionUtil.isNotEmpty(orders)) {
            for (Order order : orders) {
                // 每点合约价值
                BigDecimal pointValue = riskParamService.getParamValue(RiskNID.POINT_VALUE, order.getCommoId(), BigDecimal.class);
                // 最小波动点数
                BigDecimal waveMin = riskParamService.getParamValue(RiskNID.WAVE_POINT_MIN, order.getCommoId(), BigDecimal.class);
                //汇率
                BigDecimal exchangeRate = riskParamService.getParamValue(RiskNID.EXCHANGE_RATE, order.getCommoId(), BigDecimal.class);
                if (status == Order.STATUS.T3.code) {// 统计持仓中浮动盈亏
                    SimpleRealData realData = redisService.mapGet(CacheID.QUOTE_SIMPLEREAL_MAP, order.getCommoId().toString(), SimpleRealData.class);
                    BigDecimal np = realData.getNp();
                    if (order.getDirection() == Order.DIRECTION.T1.code) {
                        profit = profit.add((np.subtract(order.getOpenPrice())).multiply(pointValue).divide(waveMin, 4, BigDecimal.ROUND_HALF_UP).multiply(exchangeRate));
                    } else {
                        profit = profit.add((order.getOpenPrice().subtract(np)).multiply(pointValue).divide(waveMin, 4, BigDecimal.ROUND_HALF_UP).multiply(exchangeRate));
                    }

                } else if (status == Order.STATUS.T6.code) {// 统计已结算实际盈亏
                    profit = profit.add(order.getProfit());
                }
            }
        }
        BigDecimal finalProfit = profit;
        Map<String, Object> map = new HashMap<>();
        map.put("profit", finalProfit);
        List<Commo> commos = commoService.select(new Commo(co -> {
            co.setDelFlag(Commo.DELFLAG.NORMAL.code);
        }));
        if (CollectionUtil.isNotEmpty(commos)) {
            for (Commo commo : commos) {
                SimpleRealData realData = redisService.mapGet(CacheID.QUOTE_SIMPLEREAL_MAP, commo.getId().toString(), SimpleRealData.class);
                if (null != realData) {
                    map.put(commo.getCode(), realData.getNp());
                }
            }
        }
        return ok(map);
    }
}
